package com.gt.util.db;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSONObject;
import com.gt.util.model.SqlTemplateParseResult;
import com.gt.util.prop.PropUtil;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author WUREN
 * @desc 数据库操作工具类
 * @create 2019-01-30 15:27
 **/
public class DBUtil {
    private static Logger logger = Logger.getLogger(DBUtil.class);
    /**
     * 数据库配置名称，根据 gtxy.jdbc.properties 文件中的属性前缀区分
     */
    // 数据库语句执行完毕后自动管理连接
    private boolean autoCloseConnection = true;
    private String dbName;
    private DruidDataSource dbSource;
    private Connection connection;
    private QueryRunner queryRunner;
    //开启下划线->驼峰转换所用
    private BeanProcessor bean = new GenerousBeanProcessor();
    private RowProcessor processor = new BasicRowProcessor(bean);

    private static Map<String, DBConnection> dbConnectionMap = new HashMap<String, DBConnection>();
    // 标识是否执行了类static块代码
    private static boolean initFlag = false;

    /**
     * 为所有数据源初始化DBUtil，避免高并发极端情况下多次实例化DBConnection对象
     */
    static {
        initDatasource();
    }


    /**
     * 调用该空方法目的是为了执行类static静态块内容，初始化数据源
     */
    public static void init() {

    }

    private static synchronized void initDatasource() {
        if(!initFlag){
            // 默认数据源
            DBUtil defaultDB = new DBUtil();
            defaultDB.closeConnection();

            PropUtil propKit = new PropUtil(DBConstant.CONFIG_FILE_NAME);
            // 多个数据源名称使用逗号分隔
            String dbSourceNames = propKit.get("datasource.name");

            if(!StringUtils.isEmpty(dbSourceNames)){
                String[] names = dbSourceNames.split(",");
                if(names.length > 0){
                    for(String name:names){
                        if(!"default".equals(name.trim().toLowerCase())){
                            DBUtil util = new DBUtil(name.trim());
                            util.closeConnection();
                        }
                    }
                }
            }
            initFlag = true;
        }
    }

    public DBUtil() {
        try {
            DBConnection dbConnection = null;
            if(null == dbConnectionMap.get("default")) {
                dbConnection = new DBConnection();
                dbConnectionMap.put("default", dbConnection);
                logger.info("["+Thread.currentThread().getId()+"] 创建 [default] dbConnection");
            } else {
                dbConnection = dbConnectionMap.get("default");
            }

            dbSource = dbConnection.getDBSource();
            connection = dbConnection.getConnection();
            queryRunner = new QueryRunner(dbSource);
        } catch (Exception e) {
            logger.error("获取数据库连接失败！");
            closeConnection();
            e.printStackTrace();
        }
    }

    public DBUtil(String dbName) {
        try {
            DBConnection dbConnection = null;
            if(null == dbConnectionMap.get(dbName)) {
                dbConnection = new DBConnection(dbName);
                dbConnectionMap.put(dbName, dbConnection);
                logger.info("["+Thread.currentThread().getId()+"] 创建 ["+dbName+"] dbConnection");
            } else {
                dbConnection = dbConnectionMap.get(dbName);
            }

            dbSource = dbConnection.getDBSource();
            connection = dbConnection.getConnection();
            queryRunner = new QueryRunner(dbSource);
        } catch (Exception e) {
            logger.error("获取数据库连接失败！");
            closeConnection();
            e.printStackTrace();
        }
    }

    /**
     * 查询单值，返回值类型为 Long
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return
     */
    public Long querySingleLong(String sql, Object[] params) throws SQLException {
        return (Long)queryRunner.query(connection, sql, new ScalarHandler(), params);
    }

    /**
     * 查询单值，返回值类型为 Long
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return
     */
    public Long querySingleLong(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return querySingleLong(sql, null);
        }
        return null;
    }

    /**
     * 查询单值，返回值类型为 Double
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return
     */
    public Double querySingleDouble(String sql, Object[] params) throws SQLException {
        BigDecimal result  = (BigDecimal) queryRunner.query(connection, sql, new ScalarHandler(), params);
        return result.doubleValue();
    }

    /**
     * 查询单值，返回值类型为 Double
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return
     */
    public Double querySingleDouble(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return querySingleDouble(sql, null);
        }
        return null;

    }

    /**
     * 查询单值，返回值类型为 Float
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return
     */
    public Float querySingleFloat(String sql, Object[] params) throws SQLException {
        BigDecimal result  = (BigDecimal) queryRunner.query(connection, sql, new ScalarHandler(), params);
        return result.floatValue();
    }

    /**
     * 查询单值，返回值类型为 Float
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return
     */
    public Float querySingleFloat(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return querySingleFloat(sql, null);
        }
        return null;
    }

    /**
     * 查询单值，返回值类型为 String
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return
     */
    public String querySingleString(String sql, Object[] params) throws SQLException {
        return (String)queryRunner.query(connection, sql, new ScalarHandler(), params);
    }

    /**
     * 查询单值，返回值类型为 String
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return
     */
    public String querySingleString(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return querySingleString(sql, null);
        }
        return null;
    }

    /**
     * 查询单条记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          将返回记录封装到Map中，即使查询结果有多条记录，也只返回一条数据
     */
    public Map<String, Object> queryOne(String sql, Object[] params) throws SQLException {
        return queryRunner.query(connection, sql, new MapHandler(), params);
    }

    /**
     * 查询单条记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return          将返回记录封装到Map中，即使查询结果有多条记录，也只返回一条数据
     */
    public Map<String, Object> queryOne(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return queryRunner.query(connection, sql, new MapHandler());
        }
        return null;
    }

    /**
     * 查询单条记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @param clazz     映射对象class
     * @return          将返回记录映射到Bean对象
     */
    public <T> T queryOne(String sql, Object[] params, Class<T> clazz) throws SQLException {
        return queryRunner.query(connection, sql, new BeanHandler<>(clazz, processor), params);
    }

    /**
     * 查询单条记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @param clazz         映射对象class
     * @return              将返回记录映射到Bean对象
     */
    public <T> T queryOne(String templateName, String sqlTagName, Map<String, Object> params, Class<T> clazz) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return queryRunner.query(connection, sql, new BeanHandler<>(clazz, processor));
        }
        return null;
    }

    /**
     * 查询多条记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          将返回记录封装到Map中，再将Map集合存入List集合中
     */
    public List<Map<String,Object>> query(String sql, Object[] params) throws SQLException {
        return queryRunner.query(connection, sql, new MapListHandler(), params);
    }

    /**
     * 查询多条记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return              将返回记录封装到Map中，再将Map集合存入List集合中
     */
    public List<Map<String,Object>> query(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return queryRunner.query(connection, sql, new MapListHandler());
        }
        return null;
    }

    /**
     * 查询多条记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          将返回记录封装到Bean中，再将Bean存入List集合中
     */
    public <T> List<T> query(String sql, Object[] params, Class<T> clazz) throws SQLException {
        return queryRunner.query(connection, sql, new BeanListHandler<>(clazz, processor), params);
    }

    /**
     * 查询多条记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return              将返回记录封装到Bean中，再将Bean存入List集合中
     */
    public <T> List<T> query(String templateName, String sqlTagName, Map<String, Object> params, Class<T> clazz) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            return queryRunner.query(connection, sql, new BeanListHandler<>(clazz, processor));
        }
        return null;
    }

    /**
     * 插入一条记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          插入记录数
     * @throws SQLException
     */
    public int insert(String sql, Object[] params) throws SQLException {
        if(!StringUtils.isEmpty(sql)){
            if("insert".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql, params);
            }else{
                logger.error("sql语句不是【insert】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 插入一条记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return              插入记录数
     * @throws SQLException
     */
    public int insert(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            if("insert".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql);
            }else{
                logger.error("sql语句不是【insert】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 删除记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          删除记录数
     * @throws SQLException
     */
    public int delete(String sql, Object[] params) throws SQLException {
        if(!StringUtils.isEmpty(sql)){
            if("delete".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql, params);
            }else{
                logger.error("sql语句不是【delete】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 删除记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return              删除记录数
     * @throws SQLException
     */
    public int delete(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            if("delete".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql);
            }else{
                logger.error("sql语句不是【delete】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 更新记录
     * @param sql       完整SQL语句，绑定变量使用问号（?）占位
     * @param params    绑定变量数组，参数值与sql中的绑定变量按顺序一一对应
     * @return          满足更新条件的记录数，而不是实际受影响的记录数
     * @throws SQLException
     */
    public int update(String sql, Object[] params) throws SQLException {
        if(!StringUtils.isEmpty(sql)){
            if("update".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql, params);
            }else{
                logger.error("sql语句不是【update】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 更新记录
     * @param templateName  classpath:sql/*.sql，其中*的部分
     * @param sqlTagName    模板文件中#begin("*")其中*的部分
     * @param params        绑定变量Map，Map中的key值与SQL模板中的占位变量名对应
     * @return              满足更新条件的记录数，而不是实际受影响的记录数
     * @throws SQLException
     */
    public int update(String templateName, String sqlTagName, Map<String, Object> params) throws SQLException {
        String sql = SqlTemplateUtil.parseSql(templateName, sqlTagName, params);
        if(StringUtils.isNotEmpty(sql)){
            if("update".equals(sql.trim().toLowerCase().substring(0,6))){
                return queryRunner.update(connection, sql);
            }else{
                logger.error("sql语句不是【update】语句！");
                return 0;
            }
        }
        return 0;
    }

    /**
     * 关闭当前数据库连接
     */
    public void closeConnection(){
        try {
            if(null != connection){
                connection.close();
            }
        } catch (SQLException e) {
            logger.error("关闭数据库连接失败！");
            e.printStackTrace();
        }
    }

    /**
     * 开启数据库事务，即关闭数据库连接自动提交功能
     */
    public void beginTransaction() throws SQLException {
        if(null != connection){
            connection.setAutoCommit(false);
        }
    }

    /**
     * 提交数据库事务
     */
    public void commitTransaction() throws SQLException {
        if(null != connection){
            connection.commit();
            // 开启自动提交功能
            connection.setAutoCommit(true);
        } else{
            logger.error("数据库连接为空，无法提交事务！");
        }
    }

    /**
     * 回滚当数据库事务
     */
    public void rollbackTransaction() throws SQLException {
        if(null != connection){
            connection.rollback();
            // 开启自动提交功能
            connection.setAutoCommit(true);
        } else{
            logger.error("数据库连接为空，无法回滚事务！");
        }
    }
}
